#-*- coding: utf8 -*-

import os
import ctypes
import base64
import json
import time
import logging
import psycopg2
import sys
import requests

from ctypes import *
from Crypto.PublicKey import RSA
from Crypto.Cipher import PKCS1_v1_5
from configparser import ConfigParser


class FinanceSdk:

    def __init__(self, path):
        self.so = ctypes.CDLL(path + 'libWeWorkFinanceSdk_C.so')

    def init_sdk(self, corpid, secret):
        so = self.so
        sdk = so.NewSdk()
        res = so.Init(sdk, corpid.encode('utf8'), secret.encode('utf8'))
        self.sdk = sdk
        return res

    def destory_sdk(self):
        self.so.DestroySdk(self.sdk)

    def get_chat_data(self, seq, limit, proxy, passwd, timeout):
        func = self.so.GetChatData
        func.restype = c_int
        func.argtypes = [c_int, c_long, c_int, c_char_p, c_char_p, c_int, c_int]
        res = None
        proxy = ('' if proxy == None else proxy).encode('utf8')
        passwd = ('' if passwd == None else passwd).encode('utf8')
        slice = self.new_slice()
        if func(self.sdk, seq, limit, proxy, passwd, timeout, slice) == 0:
            res = self.get_content_from_slice(slice)
        self.free_slice(slice)
        return res

    def decrypt_data(self, encrypt_key, private_key, encrypt_msg):
        func = self.so.DecryptData
        func.restype = c_int
        func.argtypes = [c_char_p, c_char_p, c_int]
        res = None
        key = self.rsa_decrypt(private_key, encrypt_key)
        slice = self.new_slice()
        if func(key, encrypt_msg.encode('utf8'), slice) == 0:
            res = self.get_content_from_slice(slice)
        self.free_slice(slice)
        return res

    def get_media_data(self, index_buf, sdk_field, proxy, passwd, timeout,
                       media_data):
        func = self.so.GetMediaData
        func.restype = c_int
        func.argtypes = [c_int, c_char_p, c_char_p, c_char_p, c_char_p,
                         c_int, c_int]
        index_buf = ('' if index_buf == None else index_buf).encode('utf8')
        sdk_field = ('' if sdk_field == None else sdk_field).encode('utf8')
        proxy = ('' if proxy == None else proxy).encode('utf8')
        passwd = ('' if passwd == None else passwd).encode('utf8')
        return func(self.sdk, index_buf, sdk_field, proxy, passwd,
                    timeout, media_data)

    def new_slice(self):
        func = self.so.NewSlice
        func.restype = c_int
        return func()

    def get_content_from_slice(self, slice):
        func = self.so.GetContentFromSlice
        func.restype = c_char_p
        func.argtypes = [c_int]
        return func(slice)

    def free_slice(self, slice):
        func = self.so.FreeSlice
        func.restype = c_void_p
        func.argtypes = [c_int]
        func(slice)

    def new_media_data(self):
        func = self.so.NewMediaData
        func.restype = c_int
        return func()

    def get_data(self, media_data):
        func = self.so.GetData
        func.restype = c_int
        func.argtypes = [c_int]
        ptr = func(media_data)
        size = self.get_data_len(media_data)
        return string_at(ptr, size)

    def get_data_len(self, media_data):
        func = self.so.GetDataLen
        func.restype = c_int
        func.argtypes = [c_int]
        return func(media_data)

    def get_out_index_buf(self, media_data):
        func = self.so.GetOutIndexBuf
        func.restype = c_char_p
        func.argtypes = [c_int]
        return func(media_data)

    def is_media_data_finish(self, media_data):
        func = self.so.IsMediaDataFinish
        func.restype = c_int
        func.argtypes = [c_int]
        return func(media_data)

    def free_media_data(self, media_data):
        func = self.so.FreeMediaData
        func.restype = c_void_p
        func.argtypes = [c_int]
        func(media_data)

    def rsa_decrypt(self, private_key, text):
        pri_key = RSA.import_key(private_key)
        cipher = PKCS1_v1_5.new(pri_key)
        return cipher.decrypt(base64.b64decode(text), None)


class Backuper:

    def __init__(self):
        self.config = self._read_config()
        db = self.config['database']
        self.conn = psycopg2.connect(
                host=db['host'],
                port=db['port'],
                database=db['database'],
                user=db['user'],
                password=db['password'])
        self.conn.autocommit = True
        self.logger = logging.getLogger(__name__)
        self.media_types = ['image', 'video', 'voice', 'emotion', 'file']
        self.exts = {'image': 'jpg', 'video': 'mp4', 'voice': 'amr'}
        self.current_sequance = '0'

    def backup(self):
        self.current_sequance = '0'
        seq = self._get_sequance()
        sdk = FinanceSdk(self.config['backup']['root_path'])
        weixin = self.config['weixin']
        if sdk.init_sdk(weixin['corpid'], weixin['secret']) != 0:
            self.logger.error('SDK init error')
            return

        res = sdk.get_chat_data(seq, weixin['limit'],
                                weixin['proxy_url'], weixin['proxy_password'],
                                weixin['timeout'])
        if res is None:
            self.logger.error('SDK getCharData error')
            return

        chat_datas = json.loads(res)
        if chat_datas['errcode'] != 0:
            self.logger.error('SDK getCharData error: ' + str(res, encoding='utf8'))
            return

        data_list = chat_datas['chatdata']
        for item in data_list:
            key = item['encrypt_random_key']
            msg = item['encrypt_chat_msg']
            res = sdk.decrypt_data(key, weixin['private_key'], msg)
            if res is None:
                self.logger.error('SDK decryptData error: ' + str(item['seq']))
            else:
                # 15772920743612493791_1583075135: \x0b
                if isinstance(res, bytes):
                    res = res.decode('utf-8').replace('\x0b', '\\t')
                data = json.loads(res)
                self.logger.info('SDK decryptData: ' +
                        str(item['seq']) + '-' + data['msgtype'])
                self.save_message(data, item['seq'])
                if 'seq' not in data:
                    data['seq'] = item['seq']
                file_path = ''
                self.current_sequance = str(data['seq'])
                if data['msgtype'] in self.media_types:
                    file_path = self.save_media_data(sdk, data)
                self.save_record(sdk, data, file_path)

        sdk.destory_sdk()
        self.conn.close()

    def update(self, message_type, sequance, save_file=False):
        sdk = FinanceSdk(self.config['backup']['root_path'])
        weixin = self.config['weixin']
        if sdk.init_sdk(weixin['corpid'], weixin['secret']) != 0:
            self.logger.error('SDK init error')
            return

        ids = []
        cursor = self.conn.cursor()
        try:
            sql = 'select id from messages where sequance > 0'
            if message_type is not None:
                sql = sql + " and message_type = '%s'" % message_type
            if sequance is not None:
                sql = sql + " and sequance = '%s'" % sequance
            cursor.execute(sql)
            rs = cursor.fetchall()
            for record in rs:
                ids.append(record[0])
        except:
            pass
        finally:
            cursor.close()
        for message_id in ids:
            self.update_record(sdk, message_id, save_file)

        sdk.destory_sdk()
        self.conn.close()

    def save_message(self, data, seq):
        backup = self.config['backup']
        path = backup['path']
        month_path = self._format_timestamp(data['msgtime'], '%Y%m')
        path = path + month_path + '/'
        if not os.path.exists(path):
            os.makedirs(path)
        filename = self._format_timestamp(data['msgtime'], '%Y%m%d') + '.txt'
        file = open(path + filename, 'a')
        data['seq'] = seq
        file.write(json.dumps(data) + '\r\n')
        file.close()

    def save_media_data(self, sdk, data, is_raw_data=False):
        weixin = self.config['weixin']
        if is_raw_data:
            msgtype = data['msgtype']
            raw_data = data
        else:
            msgtype = data['msgtype']
            raw_data = data[msgtype]
        ext = ''
        if 'fileext' in raw_data:
            ext = raw_data['fileext']
        elif msgtype in self.exts:
            ext = self.exts[msgtype]
        elif msgtype == 'emotion':
            ext = ('gif' if raw_data['type'] == 1 else 'png')
        ext = ('' if len(ext) == 0 else ('.' + ext))
        path = self.config['backup']['path']
        month_path = self._format_timestamp(data['msgtime'], '%Y%m')
        path = path + month_path + '/media/'
        if not os.path.exists(path):
            os.makedirs(path)
        filename = msgtype + '_' + data['msgid'] + ext
        if os.path.exists(path + filename):
            os.remove(path + filename)

        file = open(path + filename, 'wb+')
        index_buf = ''
        while True:
            media_data = sdk.new_media_data()
            res = sdk.get_media_data(index_buf,
                                     raw_data['sdkfileid'],
                                     weixin['proxy_url'],
                                     weixin['proxy_password'],
                                     weixin['timeout'],
                                     media_data)
            if res != 0:
                sdk.free_media_data(media_data)
                file.close()
                if os.path.exists(path + filename):
                    os.remove(path + filename)
                # res=10010: 3 days ago
                self.logger.error('SDK getMediaData error: ' + json.dumps(data))
                self.save_error_log(json.dumps(raw_data), res)
                break
            content = sdk.get_data(media_data)
            file.write(content)
            if sdk.is_media_data_finish(media_data) == 1:
                sdk.free_media_data(media_data)
                file.close()
                break
            else:
                index_buf = str(sdk.get_out_index_buf(media_data),
                                encoding = 'utf8')
                sdk.free_media_data(media_data)
        if os.path.exists(path + filename):
            self.logger.info('media file saved: ' + path + filename)
        return (path + filename)

    def save_record(self, sdk, data, file_path):
        cursor = self.conn.cursor()
        backup_path_len = len(self.config['backup']['path'])
        if len(file_path) > backup_path_len:
            file_path = file_path[backup_path_len:]
        try:
            sql = "delete from messages where id='%s'" % (data['msgid'])
            cursor.execute(sql)
            record = {}
            record['id'] = data['msgid']
            record['sequance'] = str(data['seq'])
            record['action'] = data['action']
            record['from_user'] = data['from']
            record['to_user_list'] = ','.join(data['tolist'])
            record['room_id'] = data['roomid'] if 'roomid' in data else ''
            record['message_time'] = self._format_time(data['msgtime'])
            record['message_type'] = data['msgtype']
            record['content'] = ''
            record['raw_data'] = ''
            record['file_path'] = file_path
            record['created_at'] = self._format_time(int(time.time() * 1000))
            if data['msgtype'] in data:
                raw_data = data[data['msgtype']]
                record['raw_data'] = json.dumps(raw_data)
                if data['msgtype'] == 'text':
                    record['content'] = raw_data['content']
            elif data['msgtype'] == 'docmsg':
                raw_data = data['doc']
                record['raw_data'] = json.dumps(raw_data)
            sql = (
                    "insert into messages (id, sequance, action, from_user, "
                    "to_user_list, room_id, message_time, message_type, "
                    "content, raw_data, file_path, created_at) values ("
                    "%s, %s, %s, %s, %s, %s, %s, %s, "
                    "%s, %s, %s, %s)"
            )
            params = [record['id'], record['sequance'],
                      record['action'], record['from_user'],
                      record['to_user_list'], record['room_id'],
                      record['message_time'], record['message_type'],
                      record['content'], record['raw_data'],
                      record['file_path'], record['created_at']]
            cursor.execute(sql, params)
        except:
            pass
        finally:
            cursor.close()
        self.update_record(sdk, data['msgid'])

    def update_record(self, sdk, message_id, save_file=False):
        cursor = self.conn.cursor()
        try:
            sql = '''select sequance, message_type, raw_data, file_path,
                     extract(epoch from message_time) * 1000 as msgtime
                     from messages'''
            sql = sql + ' where id = %s'
            cursor.execute(sql, [message_id])
            rs = cursor.fetchall()
            if len(rs) > 0:
                record = rs[0]
                self.current_sequance = str(record[0])
                message_type = record[1]
                raw_data = None
                if record[2] is not None and len(record[2]) > 0:
                    raw_data = json.loads(record[2])
                file_path = record[3]
                message_time = record[4]
                content = ''
                relative_id = ''
                if save_file and message_type in self.media_types:
                    raw_data['msgid'] = message_id
                    raw_data['msgtype'] = message_type
                    raw_data['msgtime'] = message_time
                    self.save_media_data(sdk, raw_data, True)
                if message_type == 'file':
                    if 'filename' in raw_data:
                        content = '[file] ' + raw_data['filename']
                elif message_type == 'link':
                    if 'link_url' in raw_data:
                        content = '[link] '
                        if 'title' in raw_data:
                            content = content + raw_data['title'] + ' '
                            content = content + '(' + raw_data['link_url'] + ')'
                        else:
                            content = content + raw_data['link_url']
                elif message_type == 'location':
                    if 'address' in raw_data:
                        content = '[location] ' + raw_data['address']
                    elif 'title' in raw_data:
                        content = '[location] ' + raw_data['title']
                elif message_type == 'redpacket':
                    if 'wish' in raw_data:
                        content = '[redpacket] ' + raw_data['wish']
                elif message_type == 'weapp':
                    if 'displayname' in raw_data and 'title' in raw_data:
                        content = '[weapp] %s(%s)' % (raw_data['displayname'],
                                                      raw_data['title'])
                elif message_type == 'revoke':
                    relative_id = raw_data['pre_msgid']
                elif message_type == 'voice':
                    content = self._translate_voice(file_path)
                    if len(content) > 0:
                        content = '[voice] ' + content
                elif message_type == 'vote':
                    vote_type = 0
                    title = raw_data['votetitle']
                    if 'votetype' in raw_data:
                        vote_type = raw_data['votetype']
                    if vote_type == 101:
                        items = '; '.join(raw_data['voteitem'])
                        content = '[vote] + %s(%s)' % (title, items)
                    elif vote_type == 102:
                        items = '; '.join(raw_data['voteitem'])
                        content = '[vote] - %s(%s)' % (title, items)
                        sql = (
                                "select id from messages where "
                                "message_type = 'vote' and "
                                "raw_data::json->>'votetype' = '101' and "
                                "raw_data::json->>'voteid' = %s"
                        )
                        cursor.execute(sql, [raw_data['voteid']])
                        rs = cursor.fetchall()
                        if len(rs) > 0:
                            relative_id = rs[0][0]
                elif message_type == 'meeting':
                    meeting_type = 0
                    topic = raw_data['topic']
                    starttime = raw_data['starttime']
                    endtime = raw_data['endtime']
                    address = raw_data['address']
                    remarks = raw_data['remarks']
                    start_time = self._format_timestamp_utc8(starttime * 1000)
                    end_time = self._format_timestamp_utc8(endtime * 1000)
                    content = '%s(%s - %s)' % (topic, start_time, end_time)
                    if 'meetingtype' in raw_data:
                        meeting_type = raw_data['meetingtype']
                    if meeting_type == 101:
                        if len(address) > 0:
                            content = content + '\n' + address
                        if len(remarks) > 0:
                            content = content + '\n' + remarks
                    if meeting_type == 101:
                        content = '[meeting] + ' + content
                    elif meeting_type == 102:
                        status = raw_data['status']
                        status_text = '拒绝'
                        if status == 1:
                            status_text = '接受'
                        elif status == 2:
                            status_text = '暂定'
                        elif status == 5:
                            status_text = '取消'
                        content = '[meeting] - ' + content + '\n' + status_text
                        sql = (
                                "select id from messages where "
                                "message_type = 'meeting' and "
                                "raw_data::json->>'meetingtype' = '101' and "
                                "raw_data::json->>'meetingid' = %s"
                        )
                        cursor.execute(sql, [str(raw_data['meetingid'])])
                        rs = cursor.fetchall()
                        if len(rs) > 0:
                            relative_id = rs[0][0]
                    else:
                        content = '[meeting] ' + content
                elif message_type == 'calendar':
                    title = raw_data['title']
                    creatorname = raw_data['creatorname']
                    attendeename = raw_data['attendeename']
                    starttime = raw_data['starttime']
                    endtime = raw_data['endtime']
                    place = raw_data['place']
                    remarks = raw_data['remarks']
                    start_time = self._format_timestamp_utc8(starttime * 1000)
                    end_time = self._format_timestamp_utc8(endtime * 1000)
                    params = (title, creatorname, start_time, end_time)
                    content = '[calendar] %s(%s, %s - %s)' % params
                    if len(attendeename) > 0:
                        content = content + '\n' + ', '.join(attendeename)
                    if len(place) > 0:
                        content = content + '\n' + place
                    if len(remarks) > 0:
                        content = content + '\n' + remarks
                elif message_type == 'docmsg':
                    content = self.parse_doc_message(raw_data)
                elif message_type == 'chatrecord':
                    self.delete_chat_record_files(message_id)
                    for item in raw_data['item']:
                        text = self.save_chat_record(sdk, message_id, item)
                        content = content +\
                                ('' if len(content) == 0 else '\n\n') +\
                                text
                elif message_type == 'mixed':
                    self.delete_chat_record_files(message_id)
                    content, files = self.create_mixed_record(sdk, message_id,
                                                              raw_data,
                                                              message_time)
                    for file in files:
                        self.save_chat_record_file(message_id,
                                                   file['sequance'],
                                                   file['md5sum'],
                                                   file['file_path'])
                if len(relative_id) > 0:
                    sql = 'select id from messages where id = %s'
                    cursor.execute(sql, [relative_id])
                    rs = cursor.fetchall()
                    if len(rs) == 0:
                        relative_id = ''
                if len(content) > 0 or len(relative_id) > 0:
                    if len(relative_id) == 0:
                        relative_id = None
                    sql = (
                            "update messages set content = %s, relative_id = %s "
                            "where id = %s"
                    )
                    cursor.execute(sql, [content, relative_id, message_id])
        except:
            pass
        finally:
            cursor.close()

    def save_error_log(self, raw_data=None, error_code=None):
        cursor = self.conn.cursor()
        try:
            created_at = self._format_time(int(time.time() * 1000))
            sql = (
                    "insert into error_logs (sequance, "
                    "raw_data, error_code, created_at) values ("
                    "%s, %s, %s, %s)"
            )
            params = [self.current_sequance, raw_data, error_code, created_at]
            cursor.execute(sql, params)
        except:
            pass
        finally:
            cursor.close()

    def delete_chat_record_files(self, message_id):
        cursor = self.conn.cursor()
        try:
            sql = 'delete from chat_record_files where message_id = %s'
            cursor.execute(sql, [message_id])
        except:
            pass
        finally:
            cursor.close()

    def save_chat_record_file(self, message_id, sequance, md5sum, file_path):
        cursor = self.conn.cursor()
        backup_path_len = len(self.config['backup']['path'])
        if len(file_path) > backup_path_len:
            file_path = file_path[backup_path_len:]
        try:
            sql = (
                    "insert into chat_record_files (message_id, sequance, "
                    "md5sum, file_path, created_at) "
                    "values (%s, %s, %s, %s, %s)"
            )
            created_at = self._format_time(int(time.time() * 1000))
            params = [message_id, sequance, md5sum, file_path, created_at]
            cursor.execute(sql, params)
        except:
            pass
        finally:
            cursor.close()

    def save_chat_record(self, sdk, message_id, raw_data, loops=0, sequance=0):
        msgtime = raw_data['msgtime'] * 1000
        message_time = self._format_timestamp_utc8(msgtime)
        indentation = ' ' * loops * 4
        text = indentation + message_time + '\n' + indentation
        content = json.loads(raw_data['content'])
        if raw_data['type'] == 'ChatRecordText':
            text = text + content['content']
        elif raw_data['type'] == 'ChatRecordLink':
            if 'link_url' in content:
                text = text + '[link] ' + content['link_url']
        elif raw_data['type'] == 'ChatRecordLocation':
            if 'address' in content:
                text = text + '[location] ' + content['address']
            elif 'title' in content:
                text = text + '[location] ' + content['title']
        elif raw_data['type'] == 'ChatRecordMixed':
            data = json.loads(raw_data['content'])
            current_text, files = self.create_mixed_record(sdk, message_id,
                                                           data, msgtime)
            text = text + current_text
            for file in files:
                sequance = sequance + 1
                self.save_chat_record_file(message_id, sequance,
                                           file['md5sum'], file['file_path'])
        elif raw_data['type'] == 'chatrecord':
            text = text + '[chatrecord]'
            sub_raw_data = json.loads(raw_data['content'])
            loops = loops + 1
            for item in sub_raw_data['item']:
                sub_text = self.save_chat_record(sdk, message_id, item,
                                                 loops, sequance)
                text = text + '\n\n' + sub_text
        else:
            message_type = raw_data['type'][10:].lower()
            if message_type in self.media_types and 'md5sum' in content:
                sequance = sequance + 1
                md5sum = content['md5sum']
                content['msgid'] = md5sum
                content['msgtype'] = message_type
                content['msgtime'] = msgtime
                file_path = self.save_media_data(sdk, content, True)
                text = text + ('[%s]' % message_type)
                if message_type == 'file':
                    text = text + ' ' + content['filename']
                elif message_type == 'voice':
                    voice_text = self._translate_voice(file_path)
                    if len(content) > 0:
                        text = text + ' ' + voice_text
                self.save_chat_record_file(message_id, sequance,
                                           md5sum, file_path)
        return text

    def create_mixed_record(self, sdk, message_id, raw_data, message_time):
        content = ''
        files = []
        sequance = 0
        for item in raw_data['item']:
            current_type = item['type']
            if current_type == 'text':
                content = content +\
                        ('' if len(content) == 0 else '\n') +\
                        json.loads(item['content'])['content']
            else:
                content = content +\
                        ('' if len(content) == 0 else '\n') +\
                        ('[%s]' % item['type'])
                if current_type in self.media_types:
                    sequance = sequance + 1
                    item = json.loads(item['content'])
                    md5sum = item['md5sum']
                    item['msgid'] = md5sum
                    item['msgtype'] = current_type
                    item['msgtime'] = message_time
                    file_path = self.save_media_data(sdk, item, True)
                    files.append({
                        'sequance': sequance,
                        'md5sum': md5sum,
                        'file_path': file_path
                    })
        return (content, files)

    def parse_doc_message(self, raw_data):
        if raw_data is None:
            return ''
        title = raw_data['title']
        creator = raw_data['doc_creator']
        url = raw_data['link_url']
        cursor = self.conn.cursor()
        try:
            sql = '''select name from weixin_users where user_id = %s'''
            cursor.execute(sql, [creator])
            rs = cursor.fetchall()
            if len(rs) > 0:
                name = rs[0][0]
                if name is not None and len(name) > 0:
                    creator = name
        except:
            pass
        finally:
            cursor.close()
        return '[doc] %s(%s, %s)' % (title, creator, url)

    def _read_config(self):
        path = os.getcwd()
        if not path.endswith('/'):
            path = path + '/'
        config_file = path + 'config.cfg'
        cp = ConfigParser()
        cp.read(config_file)
        config = {}
        for section in cp.sections():
            config[section] = {}
            for item in cp.items(section):
                config[section][item[0]] = item[1]
        private_key_file = path + config['weixin']['private_key']
        file = open(private_key_file)
        config['weixin']['private_key'] = file.read()
        file.close()
        backup_path = config['backup']['path']
        if not backup_path.startswith('/'):
            backup_path = path + backup_path
        if not backup_path.endswith('/'):
            backup_path = backup_path + '/'
        config['backup']['root_path'] = path
        config['backup']['path'] = backup_path
        config['database']['port'] = int(config['database']['port'])
        config['weixin']['limit'] = int(config['weixin']['limit'])
        config['weixin']['timeout'] = int(config['weixin']['timeout'])
        config['backup']['interval'] = int(config['backup']['interval'])
        return config

    def _get_sequance(self):
        seq = 0
        cursor = self.conn.cursor()
        try:
            sql = 'select max(sequance) from messages'
            cursor.execute(sql)
            rs = cursor.fetchall()
            if len(rs) > 0:
                seq = rs[0][0]
                seq = 0 if seq == None else seq
        except:
            pass
        finally:
            cursor.close()
        return seq

    def _translate_voice(self, file_path):
        text = ''
        client_id = self.config['baidu']['client_id']
        client_secret = self.config['baidu']['client_secret']
        url = 'https://openapi.baidu.com/oauth/2.0/token?'
        url = url + 'grant_type=client_credentials&client_id=%s&client_secret=%s'
        url = url % (client_id, client_secret)
        response = requests.get(url)
        result = json.loads(response.text)
        if 'access_token' not in result:
            return text
        access_token = result['access_token']
        file_path = self.config['backup']['path'] + file_path
        if not os.path.exists(file_path):
            return text
        data = {
            'format': 'amr',
            'rate': 8000,
            'dev_pid': 1537,
            'channel': 1,
            'token': access_token,
            'cuid': '1'
        }
        file = open(file_path, 'rb')
        content = file.read()
        data['len'] = len(content)
        data['speech'] = base64.b64encode(content).decode()
        file.close()
        data = json.dumps(data)
        headers = {'Content-Type': 'application/json'}
        url = 'http://vop.baidu.com/server_api'
        response = requests.post(url=url, headers=headers, data=data)
        result = json.loads(response.text)
        if 'err_no' in result and result['err_no'] == 0:
            text = result['result'][0]
        return text


    def _format_timestamp(self, timestamp, format):
        time_array = time.localtime(timestamp / 1000)
        return time.strftime(format, time_array)

    def _format_timestamp_utc8(self, timestamp):
        time_array = time.localtime(timestamp / 1000)
        return time.strftime('%Y-%m-%d %H:%M:%S', time_array) + '(UTC+8)'

    def _format_time(self, timestamp):
        time_array = time.localtime(timestamp / 1000)
        milliseconds = str(timestamp % 1000)
        return time.strftime('%Y-%m-%d %H:%M:%S.', time_array) + milliseconds


if __name__ == '__main__':
    logging.basicConfig(
            filename='out.log', filemode='a', level=logging.INFO,
            format='%(asctime)s [%(levelname)s] %(message)s',
            datefmt='%Y-%m-%d %H:%M:%S')
    backuper = Backuper()
    if len(sys.argv) == 1:
        backuper.backup()
        interval = backuper.config['backup']['interval']
        start_time = time.time()
        while True:
            current_time = time.time()
            if current_time - start_time < interval:
                time.sleep(1)
            else:
                backuper = Backuper()
                backuper.backup()
                start_time = time.time()
    elif sys.argv[1] == 'update':
        message_type = None
        sequance = None
        save_file = False
        if len(sys.argv) > 2:
            message_type = sys.argv[2]
        if len(sys.argv) > 3:
            sequance = sys.argv[3]
        if len(sys.argv) > 4:
            save_file = True
        backuper.update(message_type, sequance, save_file)
